﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharacterData : MonoBehaviour {

    public static System.Action OnJumpBegin;

    public static System.Func<float, float> OnFilterJumpForce;

#if UNITY_EDITOR
    public bool showGizmos = true;
#endif
    [Range(0.1f, 10f)]
    public float rayDistance = 0.35f;
    [Range(0, 10f)]
    public float gravityScale = 5f;
    [Range(10f, 40f)]
    public float jumpForce = 20;
    [Range(1f, 20f)]
    public float maxSpeed = 5f;

    public Transform cameraFollow;
    public CapsuleCollider2D capsule;

    public Rigidbody2D rg2d;
    public Animator anim;

    public bool isRight = true;
    [HideInInspector]
    public bool isGround;

    [HideInInspector]
    public bool isDisableGravity;
    [HideInInspector]
    public Vector2 prevPosition;

    public float moveSpeedAppend { get; set; }

    public bool isBodyChange { get; private set; }

    public bool iceTriggerCheck { get; set; }
    public float iceMoveDuration { get; set; }
    public IceTrigger iceTrigger { get; set; }

    float maxHor;
    float iceDuration;
    float lastHor;

    float GetJumpForce() {
        return OnFilterJumpForce != null ? OnFilterJumpForce(jumpForce) : jumpForce;
    }

    IEnumerator BodyChangeTimeCounter(float duration) {
        yield return new WaitForSeconds(duration);
        BodyChangeEnd();
    }

    void BodyChangeEnd() {
        moveSpeedAppend = 0;
        isBodyChange = false;
        anim.SetBool("isBodyChange", isBodyChange);
    }

    public void BodyChange(float speedAppend, float duration) {

        if (isBodyChange) {
            return;
        }

        moveSpeedAppend = speedAppend;
        isBodyChange = true;
        anim.SetBool("isBodyChange", isBodyChange);
        StartCoroutine(BodyChangeTimeCounter(duration));
    }

    float GetMaxSpeed() {
        return maxSpeed + moveSpeedAppend;
    }

    public void CopyValuesFrom(CharacterData other) {

        rg2d.position = other.rg2d.position;

        if (other.anim != null && anim != null) {

            if (other.anim.isInitialized && anim.isInitialized) {
                var speed = other.anim.GetFloat("speed");
                var Yspeed = other.anim.GetFloat("Yspeed");
                var Isground = other.anim.GetBool("Isground");
                anim.SetFloat("speed", speed);
                anim.SetFloat("Yspeed", Yspeed);
                anim.SetBool("Isground", Isground);
            }

        }

        isRight = other.isRight;
        transform.localScale = isRight ? Vector3.one : new Vector3(-1f, 1f, 1f);
        isGround = other.isGround;

        isDisableGravity = other.isDisableGravity;
        if (isDisableGravity) {
            rg2d.gravityScale = 0;
        } else {
            rg2d.gravityScale = gravityScale;
        }
        prevPosition = other.prevPosition;

    }

    private void Awake() {
        transform.localScale = isRight ? Vector3.one : new Vector3(-1f, 1f, 1f);

        Physics2D.queriesStartInColliders = false;
    }

    private void Reset() {
        cameraFollow = transform.Find("CameraFollow");
        capsule = GetComponentInChildren<CapsuleCollider2D>();
        rg2d = GetComponentInChildren<Rigidbody2D>();
        anim = GetComponentInChildren<Animator>();
        rg2d.gravityScale = gravityScale;
    }

    private void FixedUpdate() {

        prevPosition = rg2d.position;

        isGround = false;

        Vector2[] origins = GetRaycastOrigins(capsule);

        var gos = new List<GameObject>();

        /// 检测脚底的碰撞

        for (int i = 0; i < 3; i++) {

            var o = origins[i];

            var rh2ds = Physics2D.RaycastAll(o, Vector2.down, rayDistance);

            foreach (var rh2d in rh2ds) {

                var go = rh2d.collider.gameObject;

                if (gos.Contains(go))
                    continue;

                gos.Add(go);

                if (go.layer == LayerMask.NameToLayer("Ground")) {

                    isGround = true;
                }

            }

        }

        var isInSlopingGround = false;

        if (isGround) {

            foreach (var go in gos) {

                var sg = go.GetComponent<DisableGravity>();

                if (sg != null) {
                    isInSlopingGround = true;
                }

            }


        }

        var isInputJump = false;

        // 跳
        if ((Input.GetKey(KeyCode.Space) || Input.GetKey(KeyCode.Z)) && isGround) {
            isInputJump = true;

            if(OnJumpBegin != null) {
                OnJumpBegin();
            }

        }

        // 左右移动
        float hor = Input.GetAxis("Horizontal");

        lastHor = hor;

        if ((hor > 0f && maxHor < 0f) || (hor < 0f && maxHor > 0f)) {
            iceDuration = 0;
            maxHor = 0f;
            iceTriggerCheck = false;
        }


        if (iceTrigger != null) {
            iceTriggerCheck = true;
        }

        if (iceTriggerCheck) {

            bool noMoveInput = !Input.GetKey(KeyCode.LeftArrow) && !Input.GetKey(KeyCode.RightArrow);

            if (noMoveInput) {
                if (!Mathf.Approximately(hor, 0)) {
                    if (iceDuration <= 0f) {
                        maxHor = lastHor;
                        iceDuration = iceMoveDuration;
                    }
                }

                iceDuration -= Time.fixedDeltaTime;
                if (iceDuration <= 0f) {
                    maxHor = 0f;
                    iceTriggerCheck = false;
                } else {
                    hor = maxHor;
                    maxHor = Mathf.Lerp(maxHor, 0, Time.fixedDeltaTime);
                }

            }

        } else {
            
        }

        var isInputMove = true;

        if (Mathf.Approximately(hor, 0)) {
            isInputMove = false;
        }

        if (isInputMove) {

            Vector2 rdir = Vector2.left;

            if (hor > 0) {
                rdir = Vector2.right;
            }

        }

        // 角色左右翻转判断
        if (hor > 0 && !isRight) {
            isRight = true;
            transform.localScale = new Vector3(1, 1, 1);
        } else if (hor < 0 && isRight) {
            isRight = false;
            transform.localScale = new Vector3(-1, 1, 1);
        }

        if (!isInputJump && isInSlopingGround && !isInputMove) {
            rg2d.gravityScale = 0;
            isDisableGravity = true;
        } else {
            if (isDisableGravity) {
                rg2d.gravityScale = gravityScale;
                isDisableGravity = false;
            }
        }

        var vy = isInputJump ? GetJumpForce() : rg2d.velocity.y;

        rg2d.velocity = new Vector2(hor * GetMaxSpeed(), vy);

        if (anim != null) {
            if (anim.isInitialized) {

                float speedValue = Mathf.Abs(rg2d.velocity.x);

                if (iceDuration > 0f) {
                    speedValue = 0f;
                }

                if(iceTrigger != null) {
                    speedValue = 0f;
                }

                anim.SetFloat("speed", speedValue);
                anim.SetFloat("Yspeed", rg2d.velocity.y);
                anim.SetBool("Isground", isGround);
            }
        }

    }

    Vector2[] GetRaycastOrigins(CapsuleCollider2D capsule) {
        Vector2[] origins = new Vector2[12];
        var dist = capsule.size.x * 0.5f;
        var offset = new Vector3(0, -capsule.size.y * 0.5f + dist, 0);
        var scale = transform.lossyScale;
        var position = new Vector3(capsule.offset.x * scale.x,
                                   capsule.offset.y * scale.y, 0) + capsule.transform.position;
        var bottom = position + offset;
        var top = position - offset;
        var center = bottom + (top - bottom) * 0.5f;
        origins[0] = bottom;
        origins[1] = bottom + Vector3.left * dist;
        origins[2] = bottom + Vector3.right * dist;
        origins[3] = top;
        origins[4] = top + Vector3.left * dist;
        origins[5] = top + Vector3.right * dist;
        origins[6] = center;
        origins[7] = bottom;
        origins[8] = top;
        origins[9] = center;
        origins[10] = bottom;
        origins[11] = top;
        return origins;
    }

    void CleanVelocity() {
        rg2d.velocity = new Vector2(rg2d.velocity.x, 0);
    }

    public void Teleport(Vector2 pos, bool clearVelocity = true) {

        iceDuration = 0f;
        iceTriggerCheck = false;
        iceTrigger = null;

        if (clearVelocity) {
            CleanVelocity();
        }

        rg2d.position = pos;

    }

#if UNITY_EDITOR

    void GizmosRaycast(CapsuleCollider2D capsule) {
        if (capsule == null)
            return;
        Vector2[] origins = GetRaycastOrigins(capsule);
        var dst = new Vector2(0, rayDistance);
        var dst2 = new Vector2(rayDistance, 0);
        Gizmos.DrawLine(origins[0], origins[0] - dst);
        Gizmos.DrawLine(origins[1], origins[1] - dst);
        Gizmos.DrawLine(origins[2], origins[2] - dst);
        Gizmos.DrawLine(origins[3], origins[3] + dst);
        Gizmos.DrawLine(origins[4], origins[4] + dst);
        Gizmos.DrawLine(origins[5], origins[5] + dst);
        Gizmos.DrawLine(origins[6], origins[6] - dst2);
        Gizmos.DrawLine(origins[7], origins[7] - dst2);
        Gizmos.DrawLine(origins[8], origins[8] - dst2);
        Gizmos.DrawLine(origins[9], origins[9] + dst2);
        Gizmos.DrawLine(origins[10], origins[10] + dst2);
        Gizmos.DrawLine(origins[11], origins[11] + dst2);
    }

    private void OnDrawGizmos() {

        if (!showGizmos) {
            return;
        }

        var color = Gizmos.color;
        Gizmos.color = Color.red;

        GizmosRaycast(capsule);

        if (cameraFollow != null) {
            Gizmos.DrawWireSphere(cameraFollow.position, 0.1f);
        }

        Gizmos.color = color;
    }

    [ContextMenu("Show All Gizmos")]
    public void ShowAllGizmos() {
        var arr = FindObjectsOfType<CharacterData>();
        foreach (var b in arr) {
            b.showGizmos = true;
        }
    }

    [ContextMenu("Hide All Gizmos")]
    public void HideAllGizmos() {
        var arr = FindObjectsOfType<CharacterData>();
        foreach (var b in arr) {
            b.showGizmos = false;
        }
    }
#endif
}
